package com.atmosphere.component;

import com.atmosphere.component.base.AbstractAstronomy;
import com.atmosphere.interfaces.IAtmosphere;
import com.atmosphere.interfaces.IComponentKey;
import com.atmosphere.interfaces.component.IAstronomyComponent;
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.math.FastMath;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.VBOInfo;
import com.jme.scene.shape.Quad;
import com.jme.scene.state.BlendState;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.RenderState.StateType;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;

/**
 * <code>AstronomyComponent</code> defines the implementation of
 * the component unit that is responsible for maintaining a single
 * astronomy geometry and its rotational properties.
 *
 * @author Yi Wang (Neakor)
 * @version Creation date: 05-20-2009 15:09 EST
 * @version Modified date: 05-20-2009 18:33 EST
 */
public class AstronomyComponent extends AbstractAstronomy implements IAstronomyComponent {
	/**
	 * The geometry <code>Texture</code>.
	 */
	private Texture texture;
	/**
	 * The <code>Float</code> quad width.
	 */
	private float width;
	/**
	 * The <code>Float</code> quad height.
	 */
	private float height;
	/**
	 * The Sun <code>Quad</code>.
	 */
	private Quad geometry;
	/**
	 * The <code>Boolean</code> geometry dirty flag.
	 */
	private boolean geometryDirty;
	/**
	 * The <code>Boolean</code> texture dirty flag.
	 */
	private boolean textureDirty;

	/**
	 * Constructor of <code>AstronomyComponent</code>.
	 * @param key The <code>IComponentKey</code>.
	 */
	public AstronomyComponent(IComponentKey key) {
		super(key);
	}

	@Override
	public void initialize(Node parent, IAtmosphere atmosphere) {
		this.initGeometry();
		this.initPosition();
		this.initTexture();
		this.initRotation();
		parent.attachChild(this.node);
	}
	
	private void initGeometry() {
		// Create geometry.
		this.node.detachChild(this.geometry);
		this.geometry = new Quad(this.key.getValue(), this.width, this.height);
		this.geometry.setModelBound(new BoundingBox());
		this.geometry.updateModelBound();
		this.geometry.setIsCollidable(false);
		this.geometry.setLightCombineMode(Spatial.LightCombineMode.Off);
		this.geometry.setVBOInfo(new VBOInfo(true));
		// Rotate geometry to face properly.
		Quaternion rotation = new Quaternion();
		rotation.fromAngleAxis(90*FastMath.DEG_TO_RAD, new Vector3f(1,0,0));
		this.geometry.setLocalRotation(rotation);
		// Create blend state.
		final BlendState blend = DisplaySystem.getDisplaySystem().getRenderer().createBlendState();
		blend.setBlendEnabled(true);
		blend.setSourceFunction(BlendState.SourceFunction.SourceAlpha);
		blend.setDestinationFunction(BlendState.DestinationFunction.OneMinusSourceAlpha);
		blend.setTestEnabled(true);
		blend.setTestFunction(BlendState.TestFunction.GreaterThan);
		blend.setEnabled(true);
		this.geometry.setRenderState(blend);
		this.geometry.updateRenderState();
		// Attach to node.
		this.node.attachChild(this.geometry);
		this.geometryDirty = false;
	}

	/**
	 * Initialize the Sun and Moon position.
	 */
	private void initPosition() {
		this.geometry.getLocalTranslation().set(this.position);
		this.positionDirty = false;
	}

	/**
	 * Initialize the geometry texture.
	 */
	private void initTexture() {
		TextureState state = (TextureState)this.geometry.getRenderState(StateType.Texture);
		if(state == null) {
			state = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
			this.geometry.setRenderState(state);
		}
		state.setTexture(this.texture, 0);
		this.geometry.updateRenderState();
		this.textureDirty = false;
	}

	@Override
	public void update(float interpolation) {
		this.updateDirty();
	}
	
	/**
	 * Update dirty properties.
	 */
	private void updateDirty() {
		if(this.positionDirty) this.initPosition();
		if(this.rotationDirty) this.initRotation();
		if(this.geometryDirty) this.initGeometry();
		if(this.textureDirty) this.initTexture();
	}
	
	@Override
	public void setTexture(Texture texture) {
		this.texture = texture;
		this.textureDirty = true;
	}

	@Override
	public void setSize(float width, float height) {
		this.width = width;
		this.height = height;
		this.geometryDirty = true;
	}

	@Override
	public float getWidth() {
		return this.width;
	}

	@Override
	public float getHeight() {
		return this.height;
	}

	@Override
	public void cleanup() {
		this.node.removeFromParent();
		TextureManager.releaseTexture(this.texture);
		this.texture = null;
	}
}
